// Copyright (C) 2022 即时通讯网(52im.net) & Jack Jiang.
// The RainbowChat Project. All rights reserved.
//
// 【本产品为著作权产品，合法授权后请放心使用，禁止外传！】
// 【本次授权给：<广西木子科技有限公司>，授权编号：<NT220402151538>，代码指纹：<A.648883738.885>，技术对接人微信：<ID: Lingmuziyi>】
// 【授权寄送：<收件：李先生、地址：南宁市科园西十路11号国电智能大厦1101F、电话：17736659550、邮箱：yingshashou@vip.qq.com>】
//
// 【本系列产品在国家版权局的著作权登记信息如下】：
// 1）国家版权局登记名(简称)和权证号：RainbowChat    （证书号：软著登字第1220494号、登记号：2016SR041877）
// 2）国家版权局登记名(简称)和权证号：RainbowChat-Web（证书号：软著登字第3743440号、登记号：2019SR0322683）
// 3）国家版权局登记名(简称)和权证号：RainbowAV      （证书号：软著登字第2262004号、登记号：2017SR676720）
// 4）国家版权局登记名(简称)和权证号：MobileIMSDK-Web（证书号：软著登字第2262073号、登记号：2017SR676789）
// 5）国家版权局登记名(简称)和权证号：MobileIMSDK    （证书号：软著登字第1220581号、登记号：2016SR041964）
// * 著作权所有人：江顺/苏州网际时代信息科技有限公司
//
// 【违法或违规使用投诉和举报方式】：
// 联系邮件：jack.jiang@52im.net
// 联系微信：hellojackjiang
// 联系QQ号：413980957
// 授权说明：http://www.52im.net/thread-1115-1-1.html
// 官方社区：http://www.52im.net
package com.x52im.rainbowchat.http.file.u;

import com.x52im.rainbowchat.BaseConf;
import com.x52im.rainbowchat.http.file.dto.BigFileType;

/**
 * 通用大文件上传处理实现类（支持大文件断点分块上传）。
 * <p>
 * <b>提示：</b>大文件上传原理，详见 {@link BigUploaderRoot}中的详细说明。
 *
 * @author hst, Jack Jiang
 * @see BigFileType
 * @since 4.3
 */
public class BigFileUploder extends BigUploaderRoot {
    private static final long serialVersionUID = 1L;

    /**
     * 上传的文件要保存的目录（此目录末尾要带“/”哦）。
     *
     * @return
     */
    @Override
    public String getFileSaveDir() {
        return BigFileUploder.getCommonBigFileSaveDir();
    }

    /**
     * 支持的大文件类型
     */
    @Override
    protected int getFileType() {
        return BigFileType.FILE_TYPE_COMMON_BIG_FILE;
    }

    /**
     * 上传的通用大文件要保存的目录（此目录末尾要带“/”哦）。
     *
     * @return
     */
    public static String getCommonBigFileSaveDir() {
        return BaseConf.getInstance().getDIR_USER_BIGFILE_UPLOAD();
    }

    /**
     * 通用大文件最终保存绝对路径。
     *
     * @return
     */
    public static String getCommonBigFileSavePath(String fileMd5) {
        return BigFileUploder.getCommonBigFileSaveDir() + fileMd5;
    }

    /**
     * 返回通用大文件分块存放的临时目录。
     * <p>
     * 为了防止重复和混乱，分块文件是单独保存在形如“md5码_uid”的目录下。
     *
     * @param fileMd5
     * @param userUid
     * @return
     */
    public static String getChunckCommonBigFileDir(String fileMd5, String userUid) {
        return BigFileUploder.getCommonBigFileSaveDir() + fileMd5 + (userUid != null ? "_" + userUid : "") + "/";
    }

    @Override
    protected String getTAG() {
        return "大文件";
    }

//	/**
//	 * <p>
//	 * Apache文件上传组件在解析上传数据中的每个字段内容时，需要临时保存解析出的数据，以
//	 * 便在后面进行数据的进一步处理（保存在磁盘特定位置或插入数据库）。因为Java虚拟机默
//	 * 认可以使用的内存空间是有限的，超出限制时将会抛出“java.lang.OutOfMemoryError”错
//	 * 误。如果上传的文件很大，例如800M的文件，在内存中将无法临时保存该文件内容，Apache
//	 * 文件上传组件转而采用临时文件来保存这些数据；但如果上传的文件很小，例如600个字节的
//	 * 文件，显然将其直接保存在内存中性能会更加好些。
//	 *
//	 * <p>
//	 * setSizeThreshold方法用于设置是否将上传文件已临时文件的形式保存在磁盘的临界值（以
//	 * 字节为单位的int值），如果从没有调用该方法设置此临界值，将会采用系统默认值10KB。对
//	 * 应的getSizeThreshold() 方法用来获取此临界值。*/
//	private static final int THRESHOLD_SIZE = 1024 * 200;      // 200KB
//	private static final int MAX_FILE_SIZE = 1024 * 1024 * 200;// 200MB
//	private static final int MAX_REQUEST_SIZE = MAX_FILE_SIZE;
//
//	/** Apache fileupload组件要使用的文件上传临时目录 */
//	private String repositoryDir;
//	/** 上传成功后，文件真正保存目录（末尾不带“/”） */
//	private String uploadDir;

//	protected void doGet(HttpServletRequest request, HttpServletResponse response)
//			throws ServletException, IOException
//	{
//		doPost(request, response);
//	}
//
//	@Override
//	public void init(ServletConfig config) throws ServletException
//	{
//		repositoryDir = FileUtils.getTempDirectoryPath();
//		uploadDir = getFileSaveDir();
//
//		LoggerFactory.getLog().debug("【大文件上传】临时目录：" + repositoryDir);
//		LoggerFactory.getLog().debug("【大文件上传】文件目录：" + uploadDir);
//
//		// 如果正式保存目录不存在则创建之
//		File up = new File(uploadDir);
//		if (!up.exists())
//			up.mkdirs();
//	}
//
//	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
//	{
//		LoggerFactory.getLog().debug("[HTTP大文件上传] 正在处理大文件上传请求 。。。。。。");
//
//		BufferedOutputStream outputStream = null;
//
//		// 客户端上传的当前文件分块编号（起始编号是1）
//		Integer schunk = null;
//		// 文件总分块数
//		Integer schunks = null;
//		// 客户端传上来的文件名（文件的真正名字）
//		String name = null;
//		// 总文件的大小（而不是本次上传的“块”）
//		String totalLength = null;
//		// 总文件的MD5码（而不是本次上传的“块”）
//		String totalFileMd5 = null;
//		// 客户传上来的身份认证token，暂时为保留字段，留作日后作上传安全策略使用
//		String token = null;
//		// 上传者的uid
//		String user_uid = null;
//
//		try
//		{
//			response.setCharacterEncoding("UTF-8");
//
//			if(uploadDir == null)
//				throw new IllegalArgumentException("[HTTP大文件上传] uploadDir == null!");
//
//			// checks if the request actually contains upload file
//			if (!ServletFileUpload.isMultipartContent(request))
//			{
//				LoggerFactory.getLog().error("[HTTP大文件上传] 没有需要上传的文件"
//						+ "，ServletFileUpload.isMultipartContent(request)!=true!");
//				throw new IllegalArgumentException("[HTTP大文件上传] Not file to upload!");
//			}
//
//			// configures upload settings
//			DiskFileItemFactory factory = new DiskFileItemFactory();
//			factory.setSizeThreshold(THRESHOLD_SIZE);
//			factory.setRepository(new File(repositoryDir));
//
//			ServletFileUpload upload = new ServletFileUpload(factory);
//			upload.setSizeMax(MAX_FILE_SIZE);
//
//			List<FileItem> items = upload.parseRequest(request);
//
//			// 服务端真正保存的文件名
//			String totalFileNameSavedOnServer = null;
//			// 分块文件的临时保存位置
//			File chunckSavedDir = null;
//
//			for (FileItem item : items)
//			{
//				// 如果是文件类型
//				if (!item.isFormField())
//				{
//					String logStr = "[其它参数：schunk="+schunk+", schunks="+schunks
//							+",name="+name+", totalLength="+totalLength+", totalFileMd5="+totalFileMd5
//							+", token="+token+", user_uid="+user_uid+"]";
//					LoggerFactory.getLog().error("[HTTP大文件上传] 正在处理文件\"块\"数据，item.getName="
//							+item.getName()+logStr);
//
//					if(schunk == null
//							|| schunks ==  null
//							|| CommonUtils.isStringEmpty(name, true)
//							|| CommonUtils.isStringEmpty(totalLength, true)
//							|| CommonUtils.isStringEmpty(totalFileMd5, true))
//					{
//						throw new IllegalArgumentException("[HTTP大文件上传] 无效的form data："+logStr+"!");
//					}
//
//					// 确保使用的md5码都用小写字母
//					totalFileMd5 = totalFileMd5.toLowerCase();
//					// 要保存到碰盘上的真正文件名（目前是md5码作为文件名，防止上传重名的情况）
//					totalFileNameSavedOnServer = totalFileMd5;//name;
//
//					// 分块文件的保存目录，为了防止重复重复和混乱，单独保存在“md5码_uid”的目录下
////					chunckSavedDir = new File(uploadDir+totalFileMd5+(user_uid != null?"_"+user_uid:"")+"/");
//					chunckSavedDir = new File(BigFileUploder.getChunckFileDir(totalFileMd5, user_uid));
//
//					if(!chunckSavedDir.exists())
//					{
//						chunckSavedDir.mkdirs();
//						LoggerFactory.getLog().error("[HTTP大文件上传-第"+schunk+"/"+schunks+"块] "
//								+name+"的各分\"块\"保存目录:"+chunckSavedDir.getAbsolutePath()+"创建成功！" );
//					}
//
//					if (totalFileNameSavedOnServer != null)
//					{
//						String chunkFileName = schunk + "_" + totalFileNameSavedOnServer;
//
//						// 保存该“块”到文件中
//						File chunkSavedFile = new File(chunckSavedDir, chunkFileName);
//						item.write(chunkSavedFile);
//
//						LoggerFactory.getLog().error("[HTTP大文件上传-第"+schunk+"/"+schunks+"块] "+name+"的\"块\":"
//								+chunkSavedFile.getAbsolutePath()+"写入磁盘成功完成！" );
//					}
//				}
//				// 普通的属性字段
//				else
//				{
//					if (item.getFieldName().equals("chunk"))
//						schunk = Integer.parseInt(item.getString());
//					else if (item.getFieldName().equals("chunks"))
//						schunks = Integer.parseInt(item.getString());
//					else if (item.getFieldName().equals("name"))
//						name = new String(item.getString());
//					else if (item.getFieldName().equals("totalLength"))
//						totalLength = new String(item.getString());
//					else if (item.getFieldName().equals("totalFileMd5"))
//					{
//						totalFileMd5 = new String(item.getString());
//						// 保证使用的md5码是小写的
//						if(totalFileMd5 != null)
//							totalFileMd5 = totalFileMd5.toLowerCase();
//					}
//					else if (item.getFieldName().equals("token"))
//						token = new String(item.getString());
//					else if (item.getFieldName().equals("user_uid"))
//						user_uid = new String(item.getString());
//				}
//			}
//
//			// 当前传完的“块”是最后一块：表示整个文件分块上传完成了，开始合并成最终文件
//			if (schunk != null && schunk.intValue() == schunks.intValue())
//			{
//				LoggerFactory.getLog().error("[HTTP大文件上传-第"+schunk+"/"+schunks+"块] "+name+"的最后一块上传完成，马上合并为最终文件："
//						+uploadDir+totalFileNameSavedOnServer+" >>>>>>>>");
//
//				File totalFileNameSavedOnServerFile = new File(uploadDir, totalFileNameSavedOnServer);
//				outputStream = new BufferedOutputStream(new FileOutputStream(totalFileNameSavedOnServerFile));
//				// 遍历文件合并
//				for (int i = 1; i <= schunks; i++)
//				{
//					//System.out.println("文件合并:" + i + "/" + schunks);
//					File tempFile = new File(chunckSavedDir, i + "_" + totalFileNameSavedOnServer);
//					byte[] bytes = FileUtils.readFileToByteArray(tempFile);
//					outputStream.write(bytes);
//					outputStream.flush();
//					tempFile.delete();
//
//					LoggerFactory.getLog().error("[HTTP大文件上传-第"+schunk+"/"+schunks+"块] "+name+"的最终保存文件："
//							+uploadDir+totalFileNameSavedOnServer+"已合并完成第"+i+"块...");
//				}
//				outputStream.flush();
//
//				LoggerFactory.getLog().error("[HTTP大文件上传-第"+schunk+"/"+schunks+"块] "+name+"的最终保存文件："
//						+uploadDir+totalFileNameSavedOnServer+"已全部合并完成！");
//
//				// 将最终合并完成的文件信息存库，以备使用文件时能快速高效地进行检索
//				this.afterTotalFileUploadComplete(name, totalFileNameSavedOnServerFile.length()
//						, totalFileMd5.toLowerCase(), user_uid);
//
//				// 以下代码对于成功保存完最终有文件来说，并不重要，如果有异常发生则让它静默处理就好了
//				try
//				{
//					// 最后删除分块文件存放的临时空目录
//					if(chunckSavedDir.exists())
//					{
//						String chunckSavedDirStr = chunckSavedDir.getAbsolutePath();
//						chunckSavedDir.delete();
//						LoggerFactory.getLog().error("[HTTP大文件上传] "+name+"的分块文件临时目录："+chunckSavedDirStr+"也已成功删除完成.");
//					}
//				}
//				catch (Exception e)
//				{
//					LoggerFactory.getLog().error(e);
//				}
//			}
//
////			response.getWriter().write("");
//		}
//		catch (Exception e)
//		{
//			String logStr = "[其它参数：schunk="+schunk+", schunks="+schunks
//					+"newFileName="+name+", totalLength="+totalLength+", totalFileMd5="+totalFileMd5
//					+", token="+token+", user_uid="+user_uid+"]";
//			LoggerFactory.getLog().error("[HTTP大文件上传] 出错了，客户端上传的各参数："+logStr, e);
//			response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
//			response.getWriter().write(logStr);
//		}
//		finally
//		{
//			try
//			{
//				if (outputStream != null)
//					outputStream.close();
//			}
//			catch (Exception e)
//			{
//				LoggerFactory.getLog().error(e);
//			}
//		}
//	}
//
//	/**
//	 * 上传的文件要保存的目录（此目录末尾要带“/”哦）。
//	 *
//	 * @return
//	 */
////	public static String getFileSaveDir()
////	{
////		return BaseConf.getInstance().getDIR_USER_BIGFILE_UPLOAD();
////	}
////
////	/**
////	 * 文件最终保存绝对路径。
////	 *
////	 * @return
////	 */
////	public static String getFileSavePath(String fileMd5)
////	{
////		return BaseConf.getInstance().getDIR_USER_BIGFILE_UPLOAD()+fileMd5;
////	}
////
////	/**
////	 * 返回文件分块存放的临时目录。
////	 * <p>
////	 * 为了防止重复和混乱，分块文件是单独保存在形如“md5码_uid”的目录下。
////	 *
////	 * @param fileMd5
////	 * @param userUid
////	 * @return
////	 */
////	public static String getChunckFileDir(String fileMd5, String userUid)
////	{
////		return getFileSaveDir()+fileMd5
////				+(userUid != null?"_"+userUid:"")+"/";
////	}
//
//	/**
//	 * 将最终合并完成的文件信息存库，以备使用文件时能快速高效地进行检索。
//	 *
//	 * @param fileName
//	 * @param res_size
//	 * @param fileMd5
//	 * @param user_id
//	 * @throws Exception
//	 */
//	private void afterTotalFileUploadComplete(String fileName
//			, long res_size, String fileMd5, String user_id) throws Exception
//	{
//		String fileMd5Lowsercase = fileMd5.toLowerCase();
//		// 已经存在这条记录了
//		if(LogicProcessor2.db.queryData("select 1 from missu_big_files where res_md5='"
//				+fileMd5Lowsercase+"'").size()>0)
//		{
//			LogicProcessor2.db.update("update missu_big_files set res_file_name=?"
//					+ ",res_human_size=?,res_size=?,update_time="+DBDepend.getDefaultDatetimeFunc()
//					+ ", update_user_id=?"
//					, new Object[] {fileName, CommonUtils.getConvenientFileSize(res_size, 2)
//							, res_size, user_id}, false);
//
//		}
//		else
//		{
//			LogicProcessor2.db.update(
//					"INSERT INTO missu_big_files(res_type,res_file_name,res_human_size,res_size,res_md5,create_time,create_user_uid) " +
//					"VALUES(?,?,?,?,?, "+DBDepend.getDefaultDatetimeFunc()+",?)"
//					, new Object[]{"0", fileName, CommonUtils.getConvenientFileSize(res_size, 2), res_size, fileMd5, user_id}
//					, false);
//		}
//	}


}
